feat(client): max_decompressed_size guard against decompression bombs#1262
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1262 +/- ##
==========================================
- Coverage 84.51% 84.35% -0.17%
==========================================
Files 28 28
Lines 10690 10699 +9
==========================================
- Hits 9035 9025 -10
- Misses 1655 1674 +19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Bit of a driveby comment, but it looks like the error may trigger even if the body is not compressed? If so, the name of the kwarg and error is a bit misleading? |
…n bombs Closes #1178. A new `max_decompressed_size` request keyword caps how many bytes an auto-decompressed (gzip/deflate) response body may produce; reading past it throws `DecompressionLimitError` before the bomb inflates. `0` (default) keeps the current unbounded behavior. The limiter wraps the decompressor stream, so it fires after at most one extra read chunk (memory stays bounded near the limit) and applies to both the materialized `resp.body` and caller-owned `response_stream` sinks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r type
The _DecompressLimitReader wrapper widened _response_body_reader's return into an
abstractly-parameterized union (Union{_BodyIO, _DecompressLimitReader,
TranscodingStream...}), which broke --trim static compilation
(trim_compile_tests verifier errors on macOS/Linux). Apply max_decompressed_size
inside the chunked read loops (_read_all_response_bytes / _copy_response_bytes!)
instead, leaving the reader's type set unchanged. Same DecompressionLimitError
and behavior; trim compile passes (63/63 locally).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6cf6415 to
b5001a7
Compare
It looks a bit generic, but in the response body consume function, we first do a check if the body doesn't need to be decompressed and then follow a couple different paths that don't involve the new keyword. It's only if we do need to dcompress that we fall through to the with_response_ready path where we pass the keyword along. |
Closes #1178.
Auto-decompression (gzip/deflate) currently has no size limit, so a small compressed payload can inflate to exhaust memory (a decompression/zip bomb). This adds an opt-in
max_decompressed_sizerequest keyword:0(default) keeps the current unbounded behavior — fully backward compatible.resp.bodyand caller-ownedresponse_streamsinks.HTTP.DecompressionLimitError(with the offendinglimit) for callers to catch.Tested with a ~4 MB-of-zeros gzip bomb (a few KB on the wire): rejected under a small limit, succeeds with no/large limit, and the limit is enforced on an
IOBuffersink too.Note: scoped to HTTP response-body decompression (the documented OOM vector). WebSocket frame limits are separate (
maxframesize, see #1181).🤖 Generated with Claude Code